package utils

import (
	"bytes"
	"compress/zlib"
	"crypto/hmac"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"multimedia/conf"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type TLSSigUtils struct {
	SdkAppId int64
	key      string
}

const KEY = "DDAY_101_airborne_division_e_company:We_stand_alone_together"

func NewTLSSig() *TLSSigUtils {
	return &TLSSigUtils{
		SdkAppId: conf.ConfigFile.SdkAppId,
		key:      conf.ConfigFile.Key,
	}
}
func (t *TLSSigUtils) FlateEncode(input []byte) (result []byte) {
	var buf bytes.Buffer
	w := zlib.NewWriter(&buf)
	w.Write(input)
	w.Close()
	result = buf.Bytes()
	return result
}
func (t *TLSSigUtils) FlateDecode(input string) []byte {
	z, _ := base64.StdEncoding.DecodeString(input)
	r, _ := zlib.NewReader(bytes.NewReader(z))
	result, _ := ioutil.ReadAll(r)
	return result
}
func base64EncodeUrl(input []byte) string {
	byteArr := base64.StdEncoding.EncodeToString(input)
	safeUrl := strings.Replace(byteArr, "/", "-", -1)
	safeUrl = strings.Replace(safeUrl, "+", "*", -1)
	safeUrl = strings.Replace(safeUrl, "=", "_", -1)
	return safeUrl
}
func base64DecodeUrl(byteArr string) string {
	safeUrl := strings.Replace(byteArr, "-", "/", -1)
	safeUrl = strings.Replace(safeUrl, "*", "+", -1)
	safeUrl = strings.Replace(safeUrl, "_", "=", -1)
	return safeUrl
}
func (t *TLSSigUtils) GenUserSig(userid string, expire int64) string {
	return t.genSig(userid, expire, nil)
}

func (t *TLSSigUtils) genSig(userid string, expire int64, UserBuf []byte) string {
	base64UserBuf := ""
	if UserBuf != nil {
		str := base64.StdEncoding.EncodeToString(UserBuf)
		reg := regexp.MustCompile("\\s+")
		base64UserBuf = reg.ReplaceAllString(str, "")
	}
	currTime := time.Now().UnixMilli() / 1000
	//currTime := int64(1659689320)
	sig := t.Hmacsha256(userid, currTime, expire, base64UserBuf)
	if sig == "" {
		return ""
	}
	var marshal []byte
	if UserBuf != nil {
		var buf SigDocWithUserBuf
		buf.TLSUserbuf = base64UserBuf
		buf.TLSSig = sig
		buf.TLSExpire = expire
		buf.TLSIdentifier = userid
		buf.TLSTime = currTime
		buf.TLSSdkappid = t.SdkAppId
		buf.TLSVer = "2.0"
		marshal, _ = json.Marshal(buf)
	} else {
		var sigdoc SigDoc
		sigdoc.TLSSig = sig
		sigdoc.TLSExpire = expire
		sigdoc.TLSIdentifier = userid
		sigdoc.TLSTime = currTime
		sigdoc.TLSSdkappid = t.SdkAppId
		sigdoc.TLSVer = "2.0"
		marshal, _ = json.Marshal(sigdoc)
	}
	compressedBytes := t.FlateEncode(marshal)
	return base64EncodeUrl(compressedBytes)
}

func (t *TLSSigUtils) Hmacsha256(identifier string, currTime int64, expire int64, UserBuf string) string {
	contentToBeSigned := fmt.Sprintf("TLS.identifier:%s\nTLS.sdkappid:%d\nTLS.time:%d\nTLS.expire:%d\n",
		identifier, t.SdkAppId, currTime, expire)
	if UserBuf != "" {
		contentToBeSigned += "TLS.userbuf:" + UserBuf + "\n"
	}
	h := hmac.New(sha256.New, []byte(t.key))
	h.Write([]byte(contentToBeSigned))
	sig := base64.StdEncoding.EncodeToString(h.Sum(nil))
	reg := regexp.MustCompile("\\s+")
	return reg.ReplaceAllString(sig, "")
}

func (t *TLSSigUtils) CheckTLSSignature(userId, userSig string) error {
	input := base64DecodeUrl(userSig)
	sigByte := t.FlateDecode(input)
	sigMap := make(map[string]interface{}, 0)
	json.Unmarshal(sigByte, &sigMap)

	sigTLS := sigMap["TLS.sig"].(string)
	tlsExpire := int64(sigMap["TLS.expire"].(float64))
	tlsTime := int64(sigMap["TLS.time"].(float64))
	if time.Now().UnixMilli()/1000-tlsTime > tlsExpire {
		return errors.New("TLS sig is out of date ")
	}
	tlsUserBuf, ok := sigMap["TLS.userbuf"]
	UserBuf := ""
	if ok {
		UserBuf = tlsUserBuf.(string)
	}
	newSig := t.Hmacsha256(userId, tlsTime, tlsExpire, UserBuf)
	if newSig == sigTLS {
		return nil
	}
	return errors.New("Failed in checking sig")
}

func GetSign(password, callbackUrl, callbackArg, callExt string, users []string, TimeStamp int64) string {
	UserList := strings.Join(users, ", ")
	UserStr := fmt.Sprintf("[%s]", UserList)
	str := []string{password, callbackUrl, callbackArg, UserStr, strconv.FormatInt(TimeStamp, 10), callExt}
	msg := strings.Join(str, "$")
	h := hmac.New(sha1.New, []byte(KEY))
	h.Write([]byte(msg))
	sig := base64.StdEncoding.EncodeToString(h.Sum(nil))
	return sig
}

// 给登录成功的用户生成一个token
func GenerateUserToken(userId string, TimeStamp int64) string {
	str := []string{userId, strconv.FormatInt(TimeStamp, 10)}
	msg := strings.Join(str, "$")
	h := hmac.New(sha1.New, []byte(KEY))
	h.Write([]byte(msg))
	sig := base64.StdEncoding.EncodeToString(h.Sum(nil))
	return sig
}

// sha1加密
func Sha1(s string, salt []byte) string {
	h := sha1.New()
	h.Write([]byte(s))
	byte := h.Sum((salt))
	return hex.EncodeToString(byte)
}
